package top.heue.test.http

import android.app.Application
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.ConnectivityManager
import com.hjq.http.EasyLog
import com.hjq.http.config.IRequestHandler
import com.hjq.http.exception.*
import com.hjq.http.request.HttpRequest
import com.orhanobut.hawk.Hawk
import okhttp3.Headers
import okhttp3.Response
import okhttp3.ResponseBody
import top.heue.test.R
import java.io.IOException
import java.io.InputStream
import java.lang.reflect.GenericArrayType
import java.lang.reflect.Type
import java.net.SocketTimeoutException
import java.net.UnknownHostException


class RequestHandler(private val app: Application) : IRequestHandler {
    @Throws(Throwable::class)
    override fun requestSuccess(httpRequest: HttpRequest<*>, response: Response, type: Type): Any {
        if (Response::class.java == type) {
            return response
        }
        if (!response.isSuccessful) {
            throw ResponseException(
                String.format(
                    app.getString(R.string.http_response_error),
                    response.code(), response.message()
                ), response
            )
        }
        if (Headers::class.java == type) {
            return response.headers()
        }
        val body = response.body()
            ?: throw NullBodyException(app.getString(R.string.http_response_null_body))
        if (ResponseBody::class.java == type) {
            return body
        }

        // 如果是用数组接收，判断一下是不是用 byte[] 类型进行接收的
        if (type is GenericArrayType) {
            val genericComponentType: Type = (type).genericComponentType
            if (Byte::class.javaPrimitiveType == genericComponentType) {
                return body.bytes()
            }
        }
        if (InputStream::class.java == type) {
            return body.byteStream()
        }
        if (Bitmap::class.java == type) {
            return BitmapFactory.decodeStream(body.byteStream())
        }
        val text: String = try {
            body.string()
        } catch (e: IOException) {
            // 返回结果读取异常
            throw DataException(app.getString(R.string.http_data_explain_error), e)
        }

        // 打印这个 Json 或者文本
        EasyLog.printJson(httpRequest, text)
        return text
    }

    override fun requestFail(httpRequest: HttpRequest<*>, throwable: Throwable): Throwable {
        if (throwable is HttpException) {
            return throwable
        }
        if (throwable is SocketTimeoutException) {
            return TimeoutException(
                app.getString(R.string.http_server_out_time),
                throwable
            )
        }
        if (throwable is UnknownHostException) {
            val info =
                (app.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager).activeNetworkInfo
            // 判断网络是否连接
            return if (info != null && info.isConnected) {
                // 有连接就是服务器的问题
                ServerException(app.getString(R.string.http_server_error), throwable)
            } else NetworkException(app.getString(R.string.http_network_error), throwable)
            // 没有连接就是网络异常
        }
        return if (throwable is IOException) {
            // 出现该异常的两种情况
            // 1. 调用 EasyHttp 取消请求
            // 2. 网络请求被中断
            CancelException(app.getString(R.string.http_request_cancel), throwable)
        } else HttpException(throwable.message, throwable)
    }

    override fun downloadFail(httpRequest: HttpRequest<*>, throwable: Throwable): Throwable {
        when (throwable) {
            is ResponseException -> {
                val response = throwable.response
                throwable.setMessage(
                    String.format(
                        app.getString(R.string.http_response_error),
                        response.code(), response.message()
                    )
                )
                return throwable
            }
            is NullBodyException -> {
                throwable.setMessage(app.getString(R.string.http_response_null_body))
                return throwable
            }
            is FileMd5Exception -> {
                throwable.setMessage(app.getString(R.string.http_response_md5_error))
                return throwable
            }
            else -> return requestFail(httpRequest, throwable)
        }
    }

    override fun readCache(httpRequest: HttpRequest<*>, type: Type, cacheTime: Long): Any? {
        val cacheKey: String = HttpCacheManager.generateCacheKey(httpRequest)
        val cacheValue: String? = HttpCacheManager.readHttpCache(cacheKey)
        if (cacheValue == null || "" == cacheValue || "{}" == cacheValue) {
            return null
        }
        EasyLog.printLog(httpRequest, "----- read cache key -----")
        EasyLog.printJson(httpRequest, cacheKey)
        EasyLog.printLog(httpRequest, "----- read cache value -----")
        EasyLog.printJson(httpRequest, cacheValue)
        EasyLog.printLog(httpRequest, "cacheTime = $cacheTime")
        val cacheInvalidate: Boolean = HttpCacheManager.isCacheInvalidate(cacheKey, cacheTime)
        EasyLog.printLog(httpRequest, "cacheInvalidate = $cacheInvalidate")
        return if (cacheInvalidate) {
            // 表示缓存已经过期了，直接返回 null 给外层，表示缓存不可用
            null
        } else Hawk.deserialize(cacheValue) as Type
    }

    override fun writeCache(httpRequest: HttpRequest<*>, response: Response, result: Any): Boolean {
        val cacheKey: String = HttpCacheManager.generateCacheKey(httpRequest)
        val cacheValue: String? = Hawk.serialize(result)
        if (cacheValue == null || "" == cacheValue || "{}" == cacheValue) {
            return false
        }
        EasyLog.printLog(httpRequest, "----- write cache key -----")
        EasyLog.printJson(httpRequest, cacheKey)
        EasyLog.printLog(httpRequest, "----- write cache value -----")
        EasyLog.printJson(httpRequest, cacheValue)
        val writeHttpCacheResult: Boolean = HttpCacheManager.writeHttpCache(cacheKey, cacheValue)
        EasyLog.printLog(httpRequest, "writeHttpCacheResult = $writeHttpCacheResult")
        val refreshHttpCacheTimeResult: Boolean =
            HttpCacheManager.setHttpCacheTime(cacheKey, System.currentTimeMillis())
        EasyLog.printLog(httpRequest, "refreshHttpCacheTimeResult = $refreshHttpCacheTimeResult")
        return writeHttpCacheResult && refreshHttpCacheTimeResult
    }

    override fun clearCache() {
        HttpCacheManager.clearCache()
    }
}